﻿using System;
using System.IO;
using System.Net;
using System.Text;
using Sanelib.Core;
using Sanelib.Dto.ApiRequests;
using Sanelib.Net.Http;
using Sanelib.Services;
using Sanelib.Services.Impl;

namespace Sanelib.Dto
{
    //TODO: Use Sanelib's HTTP Client for this.
    public static class Processor
    {
        public static CookieContainer CookieContainer = new CookieContainer();

        public static TO ExecuteRequest<T, TO>(ContentType contentType, RequestType requestType, T requestObject, out ErrorResult errors) 
            where T : FormBase
            where TO : new()
        {

            var clientConfig = ConfigProvider.GetClientConfig();

            requestObject.FromAgency = clientConfig.FromAgency;
            requestObject.FromSystem = clientConfig.FromSystem;
            requestObject.MachineName = clientConfig.MachineName;
            requestObject.ToAgency = clientConfig.ToAgency;
            requestObject.ToSystem = clientConfig.ToSystem;
            requestObject.LoggedUser = "NA";
            
            errors = new ErrorResult();

            ISerialization serialization;
            string cType;
            string accepts;

            switch (contentType)
            {
                case ContentType.JSON:
                    cType = "application/json; charset=utf-8";
                    accepts = "application/json";
                    serialization = new JsonSerialization();
                    break;
                default:
                    cType = "application/xml; charset=utf-8";
                    accepts = "application/xml";
                    serialization = new CustomXmlSerializer();
                    break;
            }

            try
            {
                if(string.IsNullOrEmpty(clientConfig.ServiceUrl)) throw new Exception("Base Url not set");
                var req = (HttpWebRequest)WebRequest.Create(clientConfig.ServiceUrl + requestObject.GetApiAddress());

                req.ContentType = cType;
                req.Accept = accepts;
                req.AllowAutoRedirect = false;
                req.Method = requestType.ToString();

                req.CookieContainer = CookieContainer.Count > 0 ? CookieContainer : new CookieContainer();

                var requestBytes = new byte[0];

                if (requestType != RequestType.GET)
                {
                    requestBytes = new UTF8Encoding().GetBytes(serialization.Serialize(requestObject));
                }

                req.ContentLength = requestBytes.Length;

                if (requestBytes.Length > 0)
                {
                    var stm = req.GetRequestStream();
                    stm.Write(requestBytes, 0, requestBytes.Length);
                    stm.Close();
                }

                var resp = req.GetResponse();

                if (CookieContainer.Count == 0) CookieContainer.Add(((HttpWebResponse)resp).Cookies);

                var rStm = resp.GetResponseStream();
                if (rStm == null) throw new Exception("Response stream is null");
                var stmr = new StreamReader(rStm);
                var webResponse = stmr.ReadToEnd();

                Console.WriteLine(webResponse);
                return serialization.Deserialize<TO>(webResponse);
            }
            catch (WebException e)
            {
                using (var response = e.Response)
                {
                    var httpResponse = (HttpWebResponse)response;

                    Logger.Log(LogType.Error, typeof(Processor), string.Format("Error code: {0}", httpResponse.StatusCode));
                    using (var data = response.GetResponseStream())
                    {
                        if (data == null) return new TO();
                        var webResponse = new StreamReader(data).ReadToEnd();
                        Console.WriteLine(webResponse);
                        errors = serialization.Deserialize<ErrorResult>(webResponse);
                    }
                }
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.GetBaseException().Message);
            }
            return new TO();
        }
    }
}
